/*
* Copyright (c) 2011 Connection Road.  All rights reserved.
* This program and the accompanying material are made available
* under the terms of the Eclipse Public Licnese v1.0 which is
* available at http://www.eclipse.org/legal/epl-v10.html
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using CrSyncFilesUtils.Enums;
using Microsoft.Win32;
using SyncToy;

namespace CrSyncFilesUtils
{
    /// <summary>
    /// A collection of static methods for CrSyncFiles.
    /// </summary>
    public partial class CrSyncFilesUtility
    {

        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); // Works but hard to debug on different arch

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr BringWindowToTop(IntPtr hwnd); // Does not work

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern void SwitchToThisWindow(IntPtr hwnd, bool altTab); // Too annoying

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool SetForegroundWindow(IntPtr hWnd); // Works

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr DestroyWindow(IntPtr hwnd); //Works but applicable only to child window.

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr EndTask(IntPtr hwnd, bool ignore, bool force);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("user32")]
        public static extern IntPtr RemoveMenu(int systemMenu, int itemPosition, int flag); // Removes control box. .Net have already have them.

        public static string SyncToyRegKey = @"Software\Microsoft\Synctoy";
        public static string SyncFramework32RegKey = @"SOFTWARE\Microsoft\Microsoft Sync Framework\v2.1";
        public static string SyncFramework64RegKey = @"SOFTWARE\Wow6432Node\Microsoft\Microsoft Sync Framework\v2.1";
        //public static string SyncFrameworkAssemblyName = "Microsoft.Synchronization, Version=2.1.0.0, Culture=neutral, PublicKeyToken=89845DCD8080CC91";
        //public static string SyncFileProviderAssemblyName = "Microsoft.Synchronization.Files, Version=2.1.0.0, Culture=neutral, PublicKeyToken=89845DCD8080CC91";
        public static string DefaultHost = "www.connectionroad.com";


        /// <summary>
        /// Verifies if the Sync framework is installed
        /// </summary>
        /// <returns></returns>
        public static bool IsSyncFrameworkInstalled()
        {
            try
            {
                RegistryKey registrySyncFramework;

                switch (GetArch())
                {
                    case CrSyncArchs.X86:
                        registrySyncFramework = Registry.LocalMachine.OpenSubKey(SyncFramework32RegKey);
                        break;
                    case CrSyncArchs.X64:
                        registrySyncFramework = Registry.LocalMachine.OpenSubKey(SyncFramework64RegKey);
                        break;
                    default:
                        registrySyncFramework = Registry.LocalMachine.OpenSubKey(SyncFramework32RegKey);
                        break;
                }
                    

                return registrySyncFramework != null;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static bool IsSyncToyInstalled()
        {
            var synctoyRegKey = Registry.CurrentUser.OpenSubKey(SyncToyRegKey);
            return synctoyRegKey != null;
        }


        /// <summary>
        /// Gets the absolute path of SyncToy's database file.
        /// </summary>
        /// <returns></returns>
        public static string GetSyncToyStorageFilePath()
        {
            return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\SyncToy\\2.0\\SyncToyDirPairs.bin");
        }

        public static string GetSyncToyMetaDataDirectory()
        {
            return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\SyncToy\\2.0");
        }

        /// <summary>
        /// Get a lists of available SyncToy folder pairs.
        /// </summary>
        /// <exception cref="ApplicationException"></exception>
        /// <returns></returns>
        public static List<SyncEngineConfig> GetSyncToyFolderPairs()
        {
            var synctoyLocalAppData = GetSyncToyStorageFilePath();
            var configfiles = new List<SyncEngineConfig>();
            if (File.Exists(synctoyLocalAppData))
            {
                IFormatter binaryFormmater = new BinaryFormatter();
                using (Stream fileStream = new FileStream(synctoyLocalAppData, FileMode.Open))
                {
                    while (fileStream.Position < fileStream.Length)
                    {
                        var currentConfig = (SyncEngineConfig)binaryFormmater.Deserialize(fileStream);
                        configfiles.Add(currentConfig);
                    }
                }

            }

            return configfiles;
        }

        /// <summary>
        /// Generally saves ( actually overwrites SyncToy's store file ) the given lists of SynToy folder pairs.
        /// </summary>
        /// <exception cref="ApplicationException"></exception>
        /// <param name="synctoyPairs"></param>
        public static void SetSyncToyFolderPairs(List<SyncEngineConfig> synctoyPairs)
        {
            var synctoyLocalAppData = GetSyncToyStorageFilePath();
            IFormatter formatter = new BinaryFormatter();
            using (Stream fileStream = new FileStream(synctoyLocalAppData, FileMode.Create))
            {
                foreach (var pair in synctoyPairs)
                {
                    formatter.Serialize(fileStream, pair);
                }
            }
        }


        /// <summary>
        /// Converts  a SyncToy folder pair to CrSyncFiles compatible folder pair. All meta files import are taken care by this method.
        /// </summary>
        /// <param name="synctoyPair"></param>
        /// <param name="prefix"></param>
        /// <param name="metaDataDirectory"></param>
        /// <returns></returns>
        public static FileSyncPairInfo ConvertToCrSyncFile(SyncEngineConfig synctoyPair, string prefix, string metaDataDirectory)
        {
            
            var newFolderPair = FileSyncPairManager.CreateFolderPair(synctoyPair.Name);
            newFolderPair.ActiveForRunAll = synctoyPair.Active;
            newFolderPair.DateTimeLastSync = synctoyPair.DateTimeLastSync;
            newFolderPair.LeftFolder = FileSyncPairManager.CreateFileLocation(synctoyPair.LeftDriveType,synctoyPair.LeftDir,
                                                                              prefix,
                                                                              synctoyPair.LeftGuid.ToString(),
                                                                              metaDataDirectory);

            newFolderPair.RightFolder = FileSyncPairManager.CreateFileLocation(synctoyPair.RightDriveType,synctoyPair.RightDir,
                                                                               prefix,
                                                                               synctoyPair.RightGuid.ToString(),
                                                                               metaDataDirectory);

            newFolderPair.OptionsCompareFileStreams = synctoyPair.ComputeHash;

            newFolderPair.OptionsBackupOlderFiles = synctoyPair.BackupOlderFile;
            


            // Get excluded subfodlers)
            if (synctoyPair.ExceptionalDirs != null)
            {
                var synctoySubfolders = synctoyPair.ExceptionalDirs.LeftDirectories;
                synctoySubfolders.AddRange(synctoyPair.ExceptionalDirs.RightDirectories);
                foreach (var relativeSubFolderPath in from string synctoySubfolder in synctoySubfolders
                                                      select new FileNode(synctoySubfolder)
                                                      into fileNode select fileNode.GetRelativePathFromFolderPair(newFolderPair)
                                                      into relativeSubFolderPath where relativeSubFolderPath != null where !newFolderPair.SubDirectoryExclusionList.Contains(relativeSubFolderPath) select relativeSubFolderPath)
                {
                    newFolderPair.SubDirectoryExclusionList.Add(relativeSubFolderPath);
                }
            }

            // Excluded files
            newFolderPair.SetSearchPattern(synctoyPair.SearchPattern);

            // Included files
            newFolderPair.SetExcludePattern(synctoyPair.ExcludedPattern);

            


            switch (synctoyPair.SyncMode)
            {
                case SyncMode.Synchronize:
                    newFolderPair.FolderPairActionType = FolderPairActionTypeEnum.Synchronize;
                    break;
                case SyncMode.Echo:
                    newFolderPair.FolderPairActionType = FolderPairActionTypeEnum.Echo;
                    break;
                case SyncMode.Contribute:
                    newFolderPair.FolderPairActionType = FolderPairActionTypeEnum.Contribute;
                    break;
                default:
                    newFolderPair.FolderPairActionType = FolderPairActionTypeEnum.Synchronize;
                    break;
            }


            // Import synctoy meta data
            var syncToyLeftMetaData = GetSyncToyMetaFilePath(synctoyPair.LeftGuid);
            var syncToyRightMetaData = GetSyncToyMetaFilePath(synctoyPair.RightGuid);

            // Generate the absolute path for our metadata
            var crSyncLeftMetaFile = newFolderPair.LeftFolder.MetaFilePath;
            var crSyncRightMetaFile = newFolderPair.RightFolder.MetaFilePath;

            // Create markers when necessary
            newFolderPair.LeftFolder.CreateMarker();
            newFolderPair.RightFolder.CreateMarker();


            if (syncToyLeftMetaData != crSyncLeftMetaFile)
            {
                if (File.Exists(syncToyLeftMetaData) && (!File.Exists(crSyncLeftMetaFile)))
                {
                    File.Copy(syncToyLeftMetaData, crSyncLeftMetaFile,true);
                }
            }

            if (syncToyRightMetaData != crSyncRightMetaFile)
            {
                if (File.Exists(syncToyRightMetaData) && (!File.Exists(crSyncRightMetaFile)))
                {
                    File.Copy(syncToyRightMetaData, crSyncRightMetaFile,true);
                }
            }

            if ((synctoyPair.ExcludedFileAttributes & FileAttributes.ReadOnly) != 0)
            {
                newFolderPair.FileExcludeAttrReadOnly = true;
            }

            if ((synctoyPair.ExcludedFileAttributes & FileAttributes.Hidden) != 0)
            {
                newFolderPair.FileExcludeAttrHidden = true;
            }

            if ((synctoyPair.ExcludedFileAttributes & FileAttributes.System) != 0)
            {
                newFolderPair.FileExcludeAttrSystem = true;
            }

            newFolderPair.ImportedFromSyncToy = true;

            return newFolderPair;
        }

        public static SyncEngineConfig ConvertToSyncToy(FileSyncPairInfo crsyncPair)
        {
            var synctoyPair = new SyncEngineConfig
                                  {
                                      Name = crsyncPair.FolderPairName,
                                      Active = crsyncPair.ActiveForRunAll,
                                      DateTimeLastSync = crsyncPair.DateTimeLastSync
                                  };

            // Synchronize some options
            crsyncPair.RemoveMarkerFiles();
            crsyncPair.RemoveMetaData();
            crsyncPair.LeftFolder.FileName = string.Format("SyncToy_{0}.dat",synctoyPair.LeftGuid);
            crsyncPair.RightFolder.FileName = string.Format("SyncToy_{0}.dat", synctoyPair.RightGuid);
            crsyncPair.LeftFolder.UniqueIdentifier = string.Format("{0}", synctoyPair.LeftGuid);
            crsyncPair.RightFolder.UniqueIdentifier = string.Format("{0}", synctoyPair.RightGuid);
            synctoyPair.LeftDir = crsyncPair.LeftFolder.Path;
            synctoyPair.RightDir = crsyncPair.RightFolder.Path;
            synctoyPair.ComputeHash = crsyncPair.OptionsCompareFileStreams;
            synctoyPair.BackupOlderFile = crsyncPair.OptionsBackupOlderFiles;

            // File filters
            synctoyPair.SearchPattern = crsyncPair.GetSearchPattern();
            synctoyPair.ExcludedPattern = crsyncPair.GetExcludePattern();


            crsyncPair.ExtendedInfo.AddObjectForKey(true,"HAS_MIRROR");
            
            switch (crsyncPair.FolderPairActionType)
            {
                case FolderPairActionTypeEnum.Synchronize:
                    synctoyPair.SyncMode = SyncMode.Synchronize;
                    break;
                case FolderPairActionTypeEnum.Echo:
                    synctoyPair.SyncMode = SyncMode.Echo;
                    break;
                case FolderPairActionTypeEnum.Contribute:
                    synctoyPair.SyncMode = SyncMode.Contribute;
                    break;
                default:
                    synctoyPair.SyncMode = SyncMode.Synchronize;
                    break;
            }
            
            return synctoyPair;

        }


        /// <summary>
        /// Ensures that the destination object pair contains the most recent data from the target object pair.
        /// </summary>
        /// <param name="crsyncPair">Destination</param>
        /// <param name="synctoyPair">Target</param>
        public static void SynchronizePairsFrom(FileSyncPairInfo crsyncPair, SyncEngineConfig synctoyPair)
        {
            crsyncPair.FolderPairName = synctoyPair.Name;
            crsyncPair.ActiveForRunAll = synctoyPair.Active;
            crsyncPair.DateTimeLastSync = synctoyPair.DateTimeLastSync;
            crsyncPair.OptionsCompareFileStreams = synctoyPair.ComputeHash;

            crsyncPair.OptionsBackupOlderFiles = synctoyPair.BackupOlderFile;

            // Get excluded subfolders
            crsyncPair.SubDirectoryExclusionList.Clear();
            
            if(synctoyPair.ExceptionalDirs == null)
            {
                crsyncPair.SubDirectoryExclusionList = new List<string>();
            }
            else
            {
                var excludedSynctoyFolders = new ArrayList();
                if ((synctoyPair.ExceptionalDirs.LeftDirectories != null) && (synctoyPair.ExceptionalDirs.LeftDirectories.Count >= 1))
                {
                    excludedSynctoyFolders.AddRange(synctoyPair.ExceptionalDirs.LeftDirectories);
                }
                if ((synctoyPair.ExceptionalDirs.RightDirectories != null) && (synctoyPair.ExceptionalDirs.RightDirectories.Count >= 1))
                {
                    excludedSynctoyFolders.AddRange(synctoyPair.ExceptionalDirs.RightDirectories);
                }

                if(excludedSynctoyFolders.Count >= 1)
                {
                    foreach (var relativePath in excludedSynctoyFolders.Cast<string>().Select(subfolder => new FileNode(subfolder)).Select(fileNode => fileNode.GetRelativePathFromFolderPair(crsyncPair)).Where(relativePath => !crsyncPair.SubDirectoryExclusionList.Contains(relativePath)))
                    {
                        crsyncPair.SubDirectoryExclusionList.Add(relativePath);
                    }
                }
                else
                {
                    crsyncPair.SubDirectoryExclusionList = new List<string>();
                }

            }


            crsyncPair.SetSearchPattern(synctoyPair.SearchPattern);
            crsyncPair.SetExcludePattern(synctoyPair.ExcludedPattern);

            // Sync action);
            switch (synctoyPair.SyncMode)
            {
                case SyncMode.Synchronize:
                    crsyncPair.FolderPairActionType = FolderPairActionTypeEnum.Synchronize;
                    break;
                case SyncMode.Echo:
                    crsyncPair.FolderPairActionType = FolderPairActionTypeEnum.Echo;
                    break;
                case SyncMode.Contribute:
                    crsyncPair.FolderPairActionType = FolderPairActionTypeEnum.Contribute;
                    break;
                default:
                    crsyncPair.FolderPairActionType = FolderPairActionTypeEnum.Synchronize;
                    break;
            }

            // Attribute files

            crsyncPair.FileExcludeAttrReadOnly = (synctoyPair.ExcludedFileAttributes & FileAttributes.ReadOnly) != 0;

            crsyncPair.FileExcludeAttrHidden = (synctoyPair.ExcludedFileAttributes & FileAttributes.Hidden) != 0;

            crsyncPair.FileExcludeAttrSystem = (synctoyPair.ExcludedFileAttributes & FileAttributes.System) != 0;
            
        }

        /// <summary>
        /// Ensures that the destination object pair contains the most recent data from the target object pair.
        /// </summary>
        /// <param name="synctoyPair">Destination</param>
        /// <param name="crsyncPair">Target</param>
        public static void SynchronizePairsFrom(SyncEngineConfig synctoyPair, FileSyncPairInfo crsyncPair)
        {
            synctoyPair.Name = crsyncPair.FolderPairName;
            synctoyPair.Active = crsyncPair.ActiveForRunAll;
            synctoyPair.DateTimeLastSync = crsyncPair.DateTimeLastSync;
            synctoyPair.ComputeHash = crsyncPair.OptionsCompareFileStreams;
            synctoyPair.BackupOlderFile = crsyncPair.OptionsBackupOlderFiles;
            
            // Get excluded subfodlers
            if(crsyncPair.SubDirectoryExclusionList.Count >= 1)
            {
                synctoyPair.ExceptionalDirs = new ExceptionalDirList();
                var excludedSubfolders = crsyncPair.SubDirectoryExclusionList;
                foreach (var excludedSubfolder in excludedSubfolders)
                {
                    var leftSubfolder = new FileNode(crsyncPair.LeftFolder.Path).Join(excludedSubfolder).Path;
                    var rightSubfolder = new FileNode(crsyncPair.RightFolder.Path).Join(excludedSubfolder).Path;
                    synctoyPair.ExceptionalDirs.LeftDirectories.Add(leftSubfolder);
                    synctoyPair.ExceptionalDirs.RightDirectories.Add(rightSubfolder);
                }
            }
            else
            {
                synctoyPair.ExceptionalDirs = new ExceptionalDirList();
            }


            synctoyPair.SearchPattern = crsyncPair.GetSearchPattern();
            synctoyPair.ExcludedPattern = crsyncPair.GetExcludePattern();
            

            switch (crsyncPair.FolderPairActionType)
            {
                case FolderPairActionTypeEnum.Synchronize:
                    synctoyPair.SyncMode = SyncMode.Synchronize;
                    break;
                case FolderPairActionTypeEnum.Echo:
                    synctoyPair.SyncMode = SyncMode.Echo;
                    break;
                case FolderPairActionTypeEnum.Contribute:
                    synctoyPair.SyncMode = SyncMode.Contribute;
                    break;
                default:
                    synctoyPair.SyncMode = SyncMode.Synchronize;
                    break;
            }

            var excludeFileAttr = (FileAttributes)0;
            if (crsyncPair.FileExcludeAttrReadOnly)
            {
                excludeFileAttr = excludeFileAttr | FileAttributes.ReadOnly;
            }

            if (crsyncPair.FileExcludeAttrSystem)
            {
                excludeFileAttr = excludeFileAttr | FileAttributes.System;
            }

            if (crsyncPair.FileExcludeAttrHidden)
            {
                excludeFileAttr = excludeFileAttr | FileAttributes.Hidden;
            }

            synctoyPair.ExcludedFileAttributes = excludeFileAttr;
        }


        /// <summary>
        /// Ensures that CRSyncFiles has the most recent copy of SyncToy's folder pair.
        /// </summary>
        /// <param name="prefix"></param>
        /// <param name="metaDataDirectory"></param>
        /// <param name="crsyncPairManager"></param>
        /// <param name="synctoyPairManager"></param>
        public static void ImportAvailableSyncToyFolderPairs(string prefix,string metaDataDirectory, FileSyncPairManager crsyncPairManager, CrSyncToyManager synctoyPairManager)
        {
            if (IsSyncToyInstalled())
            {

                var synctoyFolderPairs = GetSyncToyFolderPairs();
                foreach (var synctoyFolderPair in synctoyFolderPairs)
                {

                    
                    if (crsyncPairManager.HasFolderPair(synctoyFolderPair))
                    {
                        // Update CRSyncFiles folder pair
                        var crsyncPair = crsyncPairManager.CurrentFolderPair;
                        
                        SynchronizePairsFrom(crsyncPair, synctoyFolderPair);
                        
                        crsyncPairManager.UpdateFolderPair(crsyncPair);
                        synctoyPairManager.UpdateFolderPair(synctoyFolderPair);
                        
                    }
                    else
                    {
                        // Create new CRSyncFiles folder pair
                        
                        var newFolderPair = ConvertToCrSyncFile(synctoyFolderPair, prefix,metaDataDirectory);

                        // Add only if folder pair name is different from the one already exsits.
                        if (!crsyncPairManager.HasFolderPairNamed(newFolderPair.FolderPairName))
                        {
                            
                            var availablePairs = crsyncPairManager.AvailableFolderPairs();
                            
                            availablePairs.Add(newFolderPair.UniqueIdentifier, newFolderPair);
                            
                            crsyncPairManager.UpdateFolderPairs(availablePairs);
                            synctoyPairManager.UpdateFolderPair(synctoyFolderPair);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Auto import and synchronizes existing CrSyncFiles' folder pairs to SyncToy's database.
        /// This ensures that both store files contain the most recent data to prevent any metadata corruption.
        /// </summary>
        /// <param name="metaDataDirectory"></param>
        /// <param name="crsyncPairManager"></param>
        /// <param name="synctoyPairManager"></param>
        public static void ImportAvailableCrSyncFiles(string metaDataDirectory, FileSyncPairManager crsyncPairManager, CrSyncToyManager synctoyPairManager)
        {
            if(IsSyncToyInstalled())
            {
                var crsynFolderPairs = crsyncPairManager.AvailableFolderPairs();
                var synctoyFolderPairs = GetSyncToyFolderPairs();
                if (synctoyFolderPairs.Count >= 1)
                {
                    foreach (var fileSyncPairInfo in crsynFolderPairs.Where(fileSyncPairInfo => !synctoyPairManager.HasFolderPair(fileSyncPairInfo.Value)))
                    {
                        // Since SyncToy doesn't have it.. we should probably delete it.
                        crsyncPairManager.RemoveFolderPair(fileSyncPairInfo.Value);
                    }
                }
                else
                {
                    // Synctoy's database is empty so we should copy everything in CrSyncFiles
                    foreach (var fileSyncPairInfo in crsynFolderPairs)
                    {
                        synctoyPairManager.AddFolderPair(fileSyncPairInfo.Value);
                    }
                }
            }
        }


        /// <summary>
        /// This is a supplementary method after calling various synchronization method for synchronizing both SyncToy and CrSyncFiles folder pairs.
        /// While the other method ensures the both store files must contain what others doesn't have, this ensures no orphaned pairs are left in both stores.
        /// </summary>
        /// <param name="metaDataDirectory"></param>
        /// <param name="crsyncPairManager"></param>
        /// <param name="synctoyManager"></param>
        public static void CheckForUnavailableSyncToyFolderPairs(string metaDataDirectory, FileSyncPairManager crsyncPairManager, CrSyncToyManager synctoyManager)
        {
            if(IsSyncToyInstalled())
            {
                var synctoyFolderPairs = GetSyncToyFolderPairs();
                if(synctoyFolderPairs.Count >= 1)
                {
                    var crsyncFolderPairs = crsyncPairManager.AvailableFolderPairs();
                    foreach(var pairInfo in crsyncFolderPairs)
                    {
                        var hasFolderPair = synctoyManager.HasFolderPair(pairInfo.Value);
                        if(!hasFolderPair)
                        {
                            crsyncPairManager.RemoveFolderPair(pairInfo.Value);
                        }
                    }
                }
                else
                {
                    // SyncToy's store file is empty
                    var allCrSyncFolderPairs = new List<FileSyncPairInfo>();
                    crsyncPairManager.UpdateFolderPairs(allCrSyncFolderPairs);
                }
            }
        }

        /// <summary>
        /// Just a helper method to delete marker files and meta data generated by SyncToy.
        /// </summary>
        /// <param name="synctoyPair"></param>
        public static void RemoveSupplementaryFiles(SyncEngineConfig synctoyPair)
        {
            // Remove marker files
            var leftMarker = GetSyncToyMarkerFilePath(synctoyPair.LeftDir, synctoyPair.LeftGuid);
            var rigthMarker = GetSyncToyMarkerFilePath(synctoyPair.RightDir, synctoyPair.RightGuid);

            var leftMetaFile = new FileNode(GetSyncToyMetaFilePath(synctoyPair.LeftGuid));
            var rightMetaFile = new FileNode(GetSyncToyMetaFilePath(synctoyPair.RightGuid));

            if (File.Exists(leftMarker))
            {
                File.Delete(leftMarker);
            }

            if (File.Exists(rigthMarker))
            {
                File.Delete(rigthMarker);
            }

            if(File.Exists(leftMetaFile.Path))
            {
                File.Delete(leftMetaFile.Path);
            }

            if(File.Exists(rightMetaFile.Path))
            {
                File.Delete(rightMetaFile.Path);
            }
        }

        public static void WriteLog(string message, string fileName)
        {
            using (var streamWriter = !File.Exists(fileName) ? new StreamWriter(fileName) : File.AppendText(fileName))
            {
                var dateTime = string.Format("{0:MM/dd/yyyy hh:mm:ss tt}", DateTime.Now);
                if(message.Trim().Length >= 1)
                {
                    streamWriter.WriteLine("[" + dateTime + "]  " + message);
                }
                else
                {
                    streamWriter.WriteLine(message);
                }
                streamWriter.Close();    
            }
        }

        public static string GetSyncToyMarkerFilePath(string rootReplica,Guid guid)
        {
           return Path.Combine(rootReplica, string.Format("SyncToy_{0}.dat", guid));
        }

        public static string GetSyncToyMetaFilePath(Guid guid)
        {
            return new FileNode(GetSyncToyMetaDataDirectory()).Join(string.Format("SyncToy_{0}.dat", guid)).Path;
        }

        
        

        
        public static bool IsSyncToyWindowPresent()
        {
            var processes = Process.GetProcessesByName("SyncToy");
            return processes.Length >= 1;
        }


        


        /// <summary>
        /// Retrieves synctoy's last selected/ran folder pair name.
        /// </summary>
        /// <returns></returns>
        public static string GetSyncToyLastFolderPair()
        {
            try
            {
                var syncToyReg = Registry.CurrentUser.OpenSubKey(SyncToyRegKey);
                if (syncToyReg != null)
                {
                    return (string)syncToyReg.GetValue("LastFolderPair");
                }
                return null;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public static bool SetSyncToyLastFolderPair(string lastFolderPair)
        {
            try
            {
                var syncToyReg = Registry.CurrentUser.CreateSubKey(SyncToyRegKey);
                if (syncToyReg != null)
                {
                    syncToyReg.SetValue("LastFolderPair", lastFolderPair);
                    return true;
                }
                return false;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Convenience method for getting the CRSyncFile's store file.
        /// </summary>
        /// <param name="companyName"></param>
        /// <param name="version"></param>
        /// <returns></returns>
        public static string GetStoreFilePath(string companyName, string version)
        {
            return new FileNode(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)).Join(companyName).Join("CrSyncFiles\\" + version).Join("CrSyncFiles.xml").Path;
        }

        public static string FormatBytes(long bytes)
        {
            // 1000 is no longer used by the server.
            const int scale = 1024;

            var orders = new[] { "GB", "MB", "KB", "Bytes" };

            var max = (long)Math.Pow(scale, orders.Length - 1);

            foreach (string order in orders)
            {

                if (bytes > max)
                {

                    var  formatedString =  string.Format("{0:##.##} {1}", decimal.Divide(bytes, max), order);
                    return formatedString;
                }

                max /= scale;

            }

            return "0 Bytes";

        }

        /// <summary>
        ///  This Function is specifically handling the Product Offerings of 50MB, 2GB, 5GB and 30GB.
        ///  This Function will be cleaned up later, when rounding rules are determined. (Miles)
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string FormatBytesProducts(long bytes)
        {
            // 1000 is no longer used by the server
            const int scale = 1024;

            var orders = new[] { "GB", "MB", "KB", "Bytes" };

            var max = (long)Math.Pow(scale, orders.Length - 1);

            foreach (string order in orders)
            {

                if (bytes > max)
                {
                    var totalBytes = decimal.Divide(bytes, max);

                    // 2GB Plan
                    if (totalBytes >= 1 && totalBytes <= 3)
                    {
                        totalBytes = 2;
                    }

                    // 5GB Plan
                    if (totalBytes >= 4 && totalBytes <= 6)
                    {
                        totalBytes = 5;
                    }

                    // 30GB Plan
                    if (totalBytes >= 29 && totalBytes <= 32)
                    {
                        totalBytes = 30;
                    }

                    // Free 50MB Plan
                    if (totalBytes >= 49 && totalBytes <= 51)
                    {
                        totalBytes = 50;
                    }

                    var formatedString = string.Format("{0:##} {1}", totalBytes , order);
                    return formatedString;
                }

                max /= scale;

            }

            return "0 Bytes";

        }


        /// <summary>
        /// Converts a string to hexadecimal values in form of string separated by a dot "."
        /// </summary>
        /// <param name="stringToConvert"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        public static string ConvertToHex(string stringToConvert, string separator = ".")
        {
            try
            {
                var hextString = "";
                var charArray = stringToConvert.ToCharArray();
                
                foreach(var c in charArray)
                {
                    var num = Convert.ToInt32(c);
                    hextString += string.Format("{0:X}{1}", num + 1, separator);
                }

                if (hextString.StartsWith(separator))
                {
                    hextString = hextString.Substring(0);
                }

                if (hextString.EndsWith(separator))
                {
                    hextString = hextString.Substring(0, hextString.Length - 1);
                }

                return hextString;
            }
            catch (Exception)
            {
                return "";
            }
        }


        /// <summary>
        /// Converts a series of hexadecimal separated by "." back to the actual string value.
        /// </summary>
        /// <param name="stringOfHex"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        public static string ConvertHexToString(string stringOfHex, char separator = '.')
        {
            try
            {
                if (!string.IsNullOrEmpty(stringOfHex))
                {
                    var splitHex = stringOfHex.Split(separator);
                    //var stringValue = splitHex.Select(s => (char)int.Parse(s, NumberStyles.HexNumber)).Aggregate("", (current, c) => current + string.Format("{0}", c));

                    var stringValue = "";
                    foreach (var s in splitHex)
                    {
                        var num = int.Parse(s, NumberStyles.HexNumber);
                        var c = (char)(num - 1);
                        stringValue += string.Format("{0}", c);
                    }

                    return stringValue;
                }
                return "";
            }
            catch (Exception)
            {
                return "";
            }
        }

        public static CrSyncArchs GetArch()
        {
            return IntPtr.Size >= 8 ? CrSyncArchs.X64 : CrSyncArchs.X86;
        }



        public static void WriteEventLog(string message, EventLogEntryType entryType)
        {
            // Check if we have registered an event log name and source
            var regKey = Registry.LocalMachine.OpenSubKey(@"\SYSTEM\CurrentControlSet\services\eventlog\ConnectionRoad");
            if(regKey == null)
            {
                Registry.LocalMachine.CreateSubKey(@"\SYSTEM\CurrentControlSet\services\eventlog\ConnectionRoad");
            }

            const string eventSource = "CRSyncFiles";
            const string eventLogName = "ConnectionRoad";
            if (!EventLog.SourceExists(eventSource))
            {
                EventLog.CreateEventSource(eventSource, eventLogName);
            }

            //Logs
            EventLog.WriteEntry(eventSource, message, entryType);
        }
    }
    
}
